package com.gitee.magic.context;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.magic.context.editor.JsonArrayConverterEditor;
import com.gitee.magic.context.editor.JsonObjectConverterEditor;
import com.gitee.magic.context.editor.MapConverterEditor;
import com.gitee.magic.core.annotations.Column;
import com.gitee.magic.core.annotations.Transient;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.converter.PropertyConverter;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.json.JsonArray;
import com.gitee.magic.core.json.JsonObject;
import com.gitee.magic.core.utils.reflect.ParameterizedTypeImpl;
import com.gitee.magic.core.utils.reflect.TypeReference;
import com.gitee.magic.core.valid.ValidContext;
import com.gitee.magic.core.valid.annotation.NotNull;

/**
 * 已过期请使用ConverterEditorUtils代替
 * @author start
 *
 */
@Deprecated
public class RestConverterEditor {

	private static final Logger LOGGER = LoggerFactory.getLogger(RestConverterEditor.class);

	public static <T> T converterObjectMap(Class<T> classes, Map<String,Object> params) {
		return converterObjectMap(classes, params, null);
	}

	public static <T, R> T converterObjectMap(Class<T> classes, Map<String,Object> params, TypeReference<R> references) {
		return converterObject(classes, new JsonObject(params), references);
	}
	
	public static <T> Map<String,Object> converterToMap(T instance) {
		return converterToMap(instance, null);
	}

	/**
	 * 对象转Map
	 * @param instance
	 * @return
	 */
	public static <T>Map<String,Object> converterToMap(T instance,TypeReference<T> references){
		return converterJsonObject(instance, references).toMap();
	}
	
	public static <T> List<T> converterList(Class<T> classes, JsonArray jsons) {
		return converterList(classes, jsons, null);
	}
	
	public static <T>JsonArray converterJsonArray(List<T> items) {
		JsonArray array=new JsonArray();
		for(T i:items) {
			array.put(RestConverterEditor.converterToMap(i));
		}
		return array;
	}

	@SuppressWarnings("unchecked")
	public static <T> List<T> converterList(Class<T> classes, JsonArray jsons, TypeReference<T> references) {
		AbstractConverterEditor<?> editor = PropertyConverterEditor.getBasicConverter(classes);
		List<T> lists = new ArrayList<>();
		for (int i = 0; i < jsons.length(); i++) {
			if(jsons.isNull(i)) {
				continue;
			}
			if (editor != null) {
				editor.restore(jsons.get(i));
				lists.add((T) editor.getValue());
			} else {
				JsonObject obj = jsons.getJsonObject(i);
				lists.add(converterObject(classes, obj, references));
			}
		}
		return lists;
	}
	
	public static <T> T converterObject(Class<T> classes, JsonObject json) {
		return converterObject(classes, json, null);
	}

	/**
	 * JSONObject转Object
	 * 
	 * @return
	 */
	public static <T, R> T converterObject(Class<T> classes, JsonObject json, TypeReference<R> references) {
		T instance = null;
		try {
			instance = classes.newInstance();
		} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
			throw new ApplicationException(e);
		}
		Class<?> prototype = classes;
		do {
			Field[] fields = prototype.getDeclaredFields();
			for (Field field : fields) {
				if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if (field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName = field.getName();
				PropertyDescriptor pd = null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if (LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法", prototype, field.getType(),
								fieldName);
					}
					throw new ApplicationException(e);
				}
				// 别名
				if (field.isAnnotationPresent(Column.class)) {
					Column alias = field.getAnnotation(Column.class);
					fieldName = alias.value();
				}
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("JSON字段名{}，类字段名：{}", fieldName, field.getName());
				}
				Object value = null;
				// 判断是否存在当前KeyValue对
				if (json.has(fieldName)) {
					if (json.isNull(fieldName)) {
						ValidContext.verify(field.getAnnotation(NotNull.class), null,fieldName);
					} else {
						value = json.get(fieldName);
					}
				}
				boolean isContinue = false;
				if (value == null) {
					try {
						// 获取默认值
						value = pd.getReadMethod().invoke(instance);
					} catch (IllegalAccessException e) {
						throw new ApplicationException(e);
					} catch (IllegalArgumentException e) {
						throw new ApplicationException(e);
					} catch (InvocationTargetException e) {
						throw new ApplicationException(e);
					}
					ValidContext.verify(field.getAnnotation(NotNull.class), value,fieldName);
					if (value == null) {
						// 如果默认值也为null则直接跳过
						continue;
					}
					isContinue = true;
				}
				// 校验
				ValidContext.verifyField(field, value);
				if (isContinue){
					continue;
				}
				AbstractConverterEditor<?> convertObj = getConverterEditor(field, value, references);
				Object targetValue = null;
				if (convertObj == null) {
					if (references == null) {
						convertObj = getConverterEditor(field);
					}
//					if(convertObj==null) {
//						convertObj=PropertyConverterEditor.getBasicConverter(value.getClass());
//					}
					if (convertObj == null) {
						throw new ApplicationException("Class[" + prototype + "],Type:[" + field.getType() + "],Field:["
								+ field.getName() + "],Value:[" + value.getClass() + "]，无法进行转换");
					}
				}
				convertObj.restore(value);
				targetValue = convertObj.getValue();
				try {
					pd.getWriteMethod().invoke(instance, targetValue);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
			}
		} while (!(prototype = prototype.getSuperclass()).equals(Object.class));
		return instance;
	}

	public static <T> JsonObject converterJsonObject(T instance) {
		return converterJsonObject(instance, null);
	}

	/**
	 * 对象转JSONObject
	 * @param instance
	 * @return
	 */
	public static <T>JsonObject converterJsonObject(T instance,TypeReference<T> references){
		JsonObject json=new JsonObject();
		Class<?> prototype=instance.getClass();
		do {
			Field[] fields=prototype.getDeclaredFields();
			for(Field field:fields) {
				if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				String fieldName=field.getName();
				PropertyDescriptor pd=null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if(LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
					}
					throw new ApplicationException(e);
				}
				//别名
				if(field.isAnnotationPresent(Column.class)) {
					Column alias=field.getAnnotation(Column.class);
					fieldName=alias.value();
				}
				Object value=null;
				try {
					//获取默认值
					value=pd.getReadMethod().invoke(instance);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
				//NULL直接跳过
				if(value==null) {
					continue;
				}
				AbstractConverterEditor<?> convertObj=getConverterEditor(field, value,references);
				Object targetValue=null;
				if(convertObj==null) {
					if(List.class.isAssignableFrom(value.getClass())) {
						JsonArray targetValues=new JsonArray();
						for(Object obj:(List<?>)value){
							AbstractConverterEditor<?> editor=PropertyConverterEditor.getBasicConverter(obj.getClass());
							if(editor!=null) {
								editor.setValue(obj);
								targetValues.put(editor.getSource());
							}else {
								targetValues.put(converterJsonObject(obj));
							}
						}
						targetValue=targetValues;
					}else if(Map.class.isAssignableFrom(value.getClass())) {
						JsonObject targetValues=new JsonObject();
						Map<?,?> param=((Map<?,?>)value);
						for(Object key:param.keySet()){
							Object val=param.get(key);
							AbstractConverterEditor<?> keditor=PropertyConverterEditor.getBasicConverter(key.getClass());
							keditor.setValue(key);
							AbstractConverterEditor<?> veditor=PropertyConverterEditor.getBasicConverter(val.getClass());
							veditor.setValue(val);
							targetValues.put(String.valueOf(keditor.getSource()), veditor.getSource());
						}
						targetValue=targetValues;
					}else{
						AbstractConverterEditor<?> editor=PropertyConverterEditor.getBasicConverter(value.getClass());
						if(editor!=null) {
							editor.setValue(value);
							targetValue=editor.getSource();
						}else {
							targetValue=converterJsonObject(value);
						}
					}
				}else {
					convertObj.setValue(value);
					targetValue=convertObj.getSource();
				}
				json.put(fieldName, targetValue);
			}
		}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
		return json;
	}

	public static AbstractConverterEditor<?> getConverterEditor(Field field) {
		Class<?> dataType = field.getType();
		Type genericType = field.getGenericType();
		if (genericType == null) {

		} else if (genericType instanceof ParameterizedType) {
			if (List.class.isAssignableFrom(dataType)) {
				ParameterizedType pt = (ParameterizedType) genericType;
				Class<?> approveClazz = (Class<?>) pt.getActualTypeArguments()[0];
				return new JsonArrayConverterEditor(approveClazz);
			}
			if (Map.class.isAssignableFrom(dataType)) {
				ParameterizedType pt = (ParameterizedType) genericType;
				Class<?> kapproveClazz = (Class<?>) pt.getActualTypeArguments()[0];
				Class<?> vapproveClazz = (Class<?>) pt.getActualTypeArguments()[1];
				return new MapConverterEditor(kapproveClazz, vapproveClazz);
			}
		} else {
			return new JsonObjectConverterEditor(dataType);
		}
		return null;
	}

	public static AbstractConverterEditor<?> getConverterEditor(Field field, Object value,
			TypeReference<?> references) {
		PropertyConverter convert = null;
		// 字段上获取转换器
		convert = field.getAnnotation(PropertyConverter.class);
		Class<?> dataType = field.getType();
		// 数据类型上获取转换器
		if (convert == null) {
			convert = dataType.getAnnotation(PropertyConverter.class);
		}
		if (convert != null) {
			return PropertyConverterEditor.newInstance(convert.value(), dataType);
		}
		// 默认定义转换器
		AbstractConverterEditor<?> editor = PropertyConverterEditor.getBasicConverter(dataType);
		if (editor != null) {
			return editor;
		}
		if (references == null) {
			return null;
		}
		Type genericType = field.getGenericType();
		return getConverterEditor(dataType, genericType, (ParameterizedTypeImpl) references.getType());
	}

	@SuppressWarnings("rawtypes")
	public static AbstractConverterEditor<?> getConverterEditor(Class<?> dataType, Type genericType,
			ParameterizedTypeImpl typeImpl) {
		if (genericType == null) {

		} else if (genericType instanceof TypeVariable || genericType instanceof ParameterizedType) {
			Map<String, Type> params = new HashMap<>(100);
			TypeVariable<?>[] typeParameters = ((Class<?>) typeImpl.getRawType()).getTypeParameters();
			for (int i = 0; i < typeParameters.length; i++) {
				TypeVariable<?> tv = typeParameters[i];
				params.put(tv.getName(), typeImpl.getActualTypeArguments()[i]);
			}
			if (genericType instanceof TypeVariable) {
				TypeVariable typeImpls = (TypeVariable) genericType;
				Type lpType = params.get(typeImpls.getName());
				if (lpType instanceof ParameterizedTypeImpl) {
					ParameterizedTypeImpl lpTypeImpl = (ParameterizedTypeImpl) lpType;
					return getConverterEditor((Class<?>) lpTypeImpl.getRawType(), lpType, lpTypeImpl);
				} else {
					AbstractConverterEditor<?> editor = PropertyConverterEditor.getBasicConverter((Class<?>) lpType);
					if (editor != null) {
						return editor;
					}
					return new JsonObjectConverterEditor((Class<?>) lpType);
				}
			} else if (genericType instanceof ParameterizedType) {
				ParameterizedType pt = (ParameterizedType) genericType;
				if (List.class.isAssignableFrom(dataType)) {
					Type type = pt.getActualTypeArguments()[0];
					if (type instanceof TypeVariable) {
						TypeVariable lpType = (TypeVariable) type;
						return new JsonArrayConverterEditor((Class<?>) params.get(lpType.getName()));
					} else {
						return new JsonArrayConverterEditor((Class<?>) type);
					}
				} else if (Map.class.isAssignableFrom(dataType)) {
					Class<?> kapproveClazz = (Class<?>) pt.getActualTypeArguments()[0];
					Class<?> vapproveClazz = (Class<?>) pt.getActualTypeArguments()[1];
					return new MapConverterEditor(kapproveClazz, vapproveClazz);
				} else {
					// 不支持其它自定义类如：User<T>的泛型类
				}
			} else {

			}
		} else {
			return new JsonObjectConverterEditor(dataType);
		}
		return null;
	}

}
